﻿// Accord Statistics Library
// The Accord.NET Framework
// http://accord-framework.net
//
// Copyright © César Souza, 2009-2017
// cesarsouza at gmail.com
//
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser General Public
//    License as published by the Free Software Foundation; either
//    version 2.1 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//    Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public
//    License along with this library; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

namespace Accord.Statistics.Models.Regression
{
    using Accord.MachineLearning;
    using Accord.Math;
    using Accord.Statistics.Kernels;
    using System;
    using Accord.Compat;
    using Accord.Statistics.Analysis;

    /// <summary>
    ///   Multivariate non-linear regression using Kernels.
    /// </summary>
    /// 
    /// <typeparam name="TKernel">The kernel function.</typeparam>
    /// 
    /// <remarks>
    ///   Note: For the moment, the only purpose of this class is to provide a IClassifier
    ///   implementation for models generated by <see cref="KernelDiscriminantAnalysis"/>.
    ///   If you would like to train Support Vector Machines for Regression problems, please
    ///   take a look at the documentation for <c>FanChenLinSupportVectorRegression</c> class.
    /// </remarks>
    /// 
    /// <example>
    /// <code source="Unit Tests\Accord.Tests.Statistics\Analysis\KernelDiscriminantAnalysisTest.cs" region="doc_learn" />
    /// </example>
    /// 
    [Serializable]
    public class MultivariateKernelRegression<TKernel> : MultipleTransformBase<double[], double>
        where TKernel : IKernel<double[]>
    {
        /// <summary>
        ///   Gets or sets the kernel function.
        /// </summary>
        /// 
        public TKernel Kernel { get; set; }

        /// <summary>
        ///   Gets or sets the original input data that is needed to 
        ///   compute the kernel (Gram) matrices for the regression.
        /// </summary>
        /// 
        public double[][] BasisVectors { get; set; }

        /// <summary>
        ///   Gets or sets the linear weights of the regression model. The
        ///   intercept term is not stored in this vector, but is instead
        ///   available through the <see cref="Intercept"/> property.
        /// </summary>
        /// 
        public double[][] Weights { get; set; }

        /// <summary>
        ///   Gets or sets the intercept value for the regression.
        /// </summary>
        /// 
        [Obsolete()]
        public double[] Intercept { get; set; }

        /// <summary>
        ///   Gets or sets the mean values (to be subtracted from samples).
        /// </summary>
        /// 
        public double[] Means { get; set; }

        /// <summary>
        ///   Gets or sets the standard deviations (to be divided from samples).
        /// </summary>
        /// 
        public double[] StandardDeviations { get; set; }

        /// <summary>
        ///   Gets or sets the means of the data in feature space (to center samples).
        /// </summary>
        /// 
        public double[] FeatureMeans { get; set; }

        /// <summary>
        ///   Gets or sets the grand mean of the data in feature space (to center samples).
        /// </summary>
        /// 
        public double FeatureGrandMean { get; set; }

        /// <summary>
        /// Applies the transformation to an input, producing an associated output.
        /// </summary>
        /// <param name="input">The input data to which the transformation should be applied.</param>
        /// <param name="result">The location where the output should be stored.</param>
        /// <returns>
        /// The output generated by applying this transformation to the given input.
        /// </returns>
        public override double[][] Transform(double[][] input, double[][] result)
        {
            if (Means != null)
                input = input.Subtract(Means, dimension: (VectorType)0);
            if (StandardDeviations != null)
                input = input.Divide(StandardDeviations, dimension: (VectorType)0);

            // Create the Kernel matrix
            var newK = Kernel.ToJagged2(x: input, y: BasisVectors);

            if (FeatureMeans != null)
                Accord.Statistics.Kernels.Kernel.Center(newK, FeatureMeans, FeatureGrandMean, result: newK);

            // Project into the kernel principal components
            return Matrix.DotWithTransposed(newK, Weights, result: result);
        }
    }


    /// <summary>
    ///   Multivariate non-linear regression using Kernels.
    /// </summary>
    /// 
    [Serializable]
    public class MultivariateKernelRegression : MultivariateKernelRegression<IKernel>
    {

    }
}
